/////////////////////////////////////////////////////////////////////////////
/*/
 *	Creator:		Joshua C. Rouzer
 *	Title:			Emitter cpp
 *	Last Edited:	07/08/2013
 *	Summary:		Handles the creation and tracking of it's particles
/*/
/////////////////////////////////////////////////////////////////////////////
#include "Emitter.h"
#include "ParticleManager.h"
#include "CSGD_TextureManager.h"
#include "../TinyXML/tinyxml.h"
#include <sstream>
/////////////////////////////////////////////////////////////////////////////
// Function		: Constructor
// Notes		: Sets up a specified emitter
/////////////////////////////////////////////////////////////////////////////
CEmitter::CEmitter(int _image, int _partWidth, int _partHeight, Shape _shape, float posX, float posY, 
	float posX2, float posY2, float delay, float _StScale, float _EndScale, float StVelX, float StVelY,
	float EndVelX, float EndVelY, float StRot, float EndRot, float _lifeMin, float _lifeMax,
	int _StA, int _StR, int _StG, int _StB, int _EndA, int _EndR, int _EndG, int _EndB, bool _loop, int MaxPart){

//	m_Particles.clear();
	m_unParticleCount = 0;
	m_fTimer = 0.0f;
	m_bSpawn = true;
	m_bInvert = false;
	m_nImage = _image;
	m_nPartWidth = _partWidth;
	m_nPartHeight = _partHeight;
	m_eShape = _shape;
	m_fSpawnDelay = delay;
	m_fStScale = _StScale;
	m_fEndScale = _EndScale;
	m_fSVelX = StVelX;
	m_fSVelY = StVelY;
	m_fEndVelX = EndVelX;
	m_fEndVelY = EndVelY;
	m_fStRot = StRot;
	m_fEndRot = EndRot;
	m_fLifeMin = _lifeMin;
	m_fLifeMax = _lifeMax;
	m_nStAlpha = _StA;
	m_nStRed = _StR;
	m_nStGreen = _StG;
	m_nStBlue = _StB;
	m_nEndAlpha = _EndA;
	m_nEndRed = _EndR;
	m_nEndGreen = _EndG;
	m_nEndBlue = _EndB;
	m_bLooping = _loop;
	m_unMaxPart = MaxPart;
	switch(_shape){
		case ESQUARE:{
			m_fPosX = posX;
			m_fPosY = posY;
			m_fPosX2 = posX2;
			m_fPosY2 = posY2;
			break;}
		case ECIRCLE:{
			m_fPosX = posX;
			m_fPosY = posY;
			m_fRange = posX2 - posX;
			break;}
		case EVLINE:{
			m_fPosX = posX;
			m_fPosY = posY;
			m_fPosX2 = posX2;
			m_fPosY2 = posY2;
			break;}
		case EHLINE:{
			m_fPosX = posX;
			m_fPosY = posY;
			m_fPosX2 = posX2;
			m_fPosY2 = posY2;
			break;}
		case EPOINT:{
			m_fPosX = posX;
			m_fPosY = posY;
			break;}
	}
}
/////////////////////////////////////////////////////////////////////////////
// Function		: Default Constructor
// Notes		: Assigns default values to all variables
/////////////////////////////////////////////////////////////////////////////
CEmitter::CEmitter(){
	m_Particles.clear();
	m_unParticleCount	= 0;
	m_unMaxPart			= 10;
	m_fSpawnDelay		= 1.0f;
	m_fTimer			= 0.0f;
	m_bLooping			= false;
	m_bInvert			= false;
	m_bSpawn			= true;
	m_nStAlpha			= 255;
	m_nStRed			= 0;
	m_nStGreen			= 0;
	m_nStBlue			= 0;
	m_nEndAlpha			= 255;
	m_nEndRed			= 0;
	m_nEndGreen			= 0;
	m_nEndBlue			= 0;
	m_fSVelX			= 2.0f;
	m_fSVelY			= 2.0f;
	m_nImage			= -1;
	m_eShape			= EPOINT;
	m_fPosX				= 0.0f;
	m_fPosY				= 0.0f;
	m_fLifeMax			= 5.0f;
	m_fLifeMin			= 5.0f;
}
/////////////////////////////////////////////////////////////////////////////
// Function		: Destructor
// Notes		: Cleans up any dynamically allocated memory
/////////////////////////////////////////////////////////////////////////////
CEmitter::~CEmitter(){
	for(unsigned int iter = 0; iter < m_Particles.size();){
		delete m_Particles[iter];
		m_Particles.erase(m_Particles.begin() + iter);
	}
	m_Particles.clear();
}
/////////////////////////////////////////////////////////////////////////////
// Function		: Operator ==
// Parameters	: pEmitter - The comparison emitter
// Notes		: Will check if the information between emitters match
/////////////////////////////////////////////////////////////////////////////
bool CEmitter::operator==(CEmitter& pEmitter){
	bool _return = true;
	//Check looping
	if(this->m_bLooping != pEmitter.m_bLooping || this->m_bInvert != pEmitter.m_bInvert){
		_return = false;
	}
	//Check particle count Max and current
	else if(this->m_unMaxPart != pEmitter.m_unMaxPart){
		_return = false;
	}
	//Check position
	else if(this->m_fPosX != pEmitter.m_fPosX || this->m_fPosY != pEmitter.m_fPosY){
		_return = false;
	}
	//Check Shape
	else if(this->m_eShape != pEmitter.m_eShape){
		_return = false;
	}
	//Check Delay
	else if(this->m_fSpawnDelay != pEmitter.m_fSpawnDelay){
		_return = false;
	}
	//Check Image (Size too)
	else if(this->m_nImage != pEmitter.m_nImage || 
		this->m_nPartWidth != pEmitter.m_nPartWidth || this->m_nPartHeight != pEmitter.m_nPartHeight){
		_return = false;
	}
	//Check Starting Info
	else if(this->m_fStScale != pEmitter.m_fStScale || 
		this->m_fSVelX != pEmitter.m_fSVelX || this->m_fSVelY != pEmitter.m_fSVelY ||
		this->m_fStRot != pEmitter.m_fStRot){
		_return = false;
	}
	//Check Ending Info
	else if(this->m_fEndScale != pEmitter.m_fEndScale || 
		this->m_fEndVelX != pEmitter.m_fEndVelX || this->m_fEndVelY != pEmitter.m_fEndVelY ||
		this->m_fEndRot != pEmitter.m_fEndRot){
		_return = false;
	}
	//Check Spawn
	else if(this->m_bSpawn != pEmitter.m_bSpawn){
		_return = false;
	}
	//Check Max and Min Life
	else if(this->m_fLifeMax != pEmitter.m_fLifeMax || this->m_fLifeMin != pEmitter.m_fLifeMin){
		_return = false;
	}
	//Check Starting Color
	else if(this->m_nStAlpha != pEmitter.m_nStAlpha || this->m_nStRed != pEmitter.m_nStRed || 
		this->m_nStGreen != pEmitter.m_nStGreen || this->m_nStBlue != pEmitter.m_nStBlue){
		_return = false;
	}
	//Check Ending Color
	else if(this->m_nEndAlpha != pEmitter.m_nEndAlpha || this->m_nEndRed != pEmitter.m_nEndRed || 
		this->m_nEndGreen != pEmitter.m_nEndGreen || this->m_nEndBlue != pEmitter.m_nEndBlue){
		_return = false;
	}
	//Check shape sub variables
	else{
		//Only need to check secondary variables
		switch(this->m_eShape){
			case ECIRCLE:{
				if(this->m_fRange != pEmitter.m_fRange){
					_return = false;
				}
				break;
			}
			case ESQUARE:{
			case EVLINE:
			case EHLINE:
				if(this->m_fPosX2 != pEmitter.m_fPosX2 || this->m_fPosY2 != pEmitter.m_fPosY2){
					_return = false;
				}
				break;
			}
			case EPOINT:{
				//Already checked position for point
				break;
			}
		}
	}
	return _return;
}
/////////////////////////////////////////////////////////////////////////////
// Function		: Clear
// Notes		: Removes all particles
/////////////////////////////////////////////////////////////////////////////
void CEmitter::Clear(){
	while(m_Particles.size() > 0){
		delete m_Particles[0];
		m_Particles.erase(m_Particles.begin());
	}
}
/////////////////////////////////////////////////////////////////////////////
// Function		: Is Finished
// Return		: If the emitter is finished
// Notes		: Checks a few settings to see if it is done
/////////////////////////////////////////////////////////////////////////////
bool CEmitter::IsFinished(){
	if(m_bLooping){
		return false;}
	if(m_unMaxPart > m_unParticleCount && m_Particles.size() < m_unMaxPart){
		return false;
	}
	else if(m_unMaxPart <= m_unParticleCount || m_Particles.size() >= m_unMaxPart){
		return true;
	}
	return false;
}
/////////////////////////////////////////////////////////////////////////////
// Function		: Spawn
// Return		: If the spawn was successful
// Notes		: Spawns another particle
/////////////////////////////////////////////////////////////////////////////
bool CEmitter::Spawn(){
	//Determine Position
	float _PosX = m_fPosX, _PosY = m_fPosY;
	switch(m_eShape){
		case EPOINT:
			//Spawn it at the default point
			break;
		case ECIRCLE:
			break;
		case ESQUARE:
			_PosX = rand() % (int)(m_fPosX2 - m_fPosX) + m_fPosX;
			_PosY = rand() % (int)(m_fPosY2 - m_fPosY) + m_fPosY;
			break;
		case EVLINE:
			_PosY = rand() % (int)(m_fPosY2 - m_fPosY) + m_fPosY;
			break;
		case EHLINE:
			_PosX = rand() % (int)(m_fPosX2 - m_fPosX) + m_fPosX;
			break;
	}
	//Determine Life
	float _Life = m_fLifeMin;
	if(m_fLifeMax != m_fLifeMin){
		if((int)(m_fLifeMax - m_fLifeMin) == 0){			
			_Life = rand() % 1 + m_fLifeMin;
		}
		else{
			_Life = rand() % (int)(m_fLifeMax - m_fLifeMin) + m_fLifeMin;
		}
	}
	//Create particle
	CParticle *part = new CParticle(this, _PosX, _PosY, _Life);
	//CParticle *part = new CParticle(this, m_fPosX, m_fPosY, m_fLifeMin);
	//Add particle to the list
	m_Particles.push_back(part);
	m_unParticleCount++;
	return true;
}
/////////////////////////////////////////////////////////////////////////////
// Function		: Despawn
// Parameters	: pParticle - The particle to be removed
// Return		: If the despawn was successful
// Notes		: Removes the particle from the list
/////////////////////////////////////////////////////////////////////////////
bool CEmitter::Despawn(CParticle& pParticle){
	for(unsigned int iter = 0; iter < m_Particles.size(); iter++){
		if(*m_Particles[iter] == pParticle){
			delete m_Particles[iter];
			m_Particles.erase(m_Particles.begin() + iter);
			return true;
		}
	}
	return false;
}
/////////////////////////////////////////////////////////////////////////////
// Function		: Update
// Parameters	: dT - Time elapsed since last update
// Notes		: Updates the particles and spawns new ones as needed
/////////////////////////////////////////////////////////////////////////////
void CEmitter::Update(float dT, CParticleManager *_Man){
	m_fTimer -= dT;
	//Check the timer for another spawn
	if(m_fTimer <= 0.0f){
		//Make sure it can still spawn particles and is cleared to spawn
		if(!IsFinished() && m_bSpawn){
			Spawn();
			m_fTimer = m_fSpawnDelay;
		}
	}
	for(unsigned int iter = 0; iter < m_Particles.size(); iter++){
		m_Particles[iter]->Update(dT, this);
	}
	//Clear dead particles
	for(unsigned int iter = 0; iter < m_Particles.size(); iter++){
		if(!m_Particles[iter]->Alive()){
			Despawn(*m_Particles[iter]);
		}
	}
	//Check if emitter is finished
	if(IsFinished() && m_unMaxPart == m_unParticleCount && m_Particles.size() == 0){
		//Then tell it to be destroyed
		_Man->RemoveEmitter(this);
		//delete this;
		return;
	}
}
/////////////////////////////////////////////////////////////////////////////
// Function		: Render All
// Notes		: Displays all particles to the screen
/////////////////////////////////////////////////////////////////////////////
void CEmitter::RenderAll(){
	RECT rect;
	SetRect(&rect, 0, 0, m_nPartWidth, m_nPartHeight);
	for(unsigned int iter = 0; iter < m_Particles.size(); iter++){
		CSGD_TextureManager::GetInstance()->Draw(GetImage(),
			(int)(m_Particles[iter]->GetPosX() - (0.5f * GetPartWidth() * m_Particles[iter]->GetScale())),
			(int)(m_Particles[iter]->GetPosY() - (0.5f * GetPartHeight() * m_Particles[iter]->GetScale())),
			m_Particles[iter]->GetScale(), m_Particles[iter]->GetScale(), &rect,
			m_nPartWidth / 2.0f, m_nPartHeight / 2.0f,
			D3DX_PI * m_Particles[iter]->GetRot(), m_Particles[iter]->GetColor());
	}
}
/////////////////////////////////////////////////////////////////////////////
// Function		: Set End Velocity
// Notes		: sets the end velocity for particles
/////////////////////////////////////////////////////////////////////////////
void CEmitter::SetEVel(float _VelX, float _VelY){
	m_fEndVelX = _VelX;
	m_fEndVelY = _VelY;
}
/////////////////////////////////////////////////////////////////////////////
// Function		: Set Start Velocity
// Notes		: sets the start velocity for particles
/////////////////////////////////////////////////////////////////////////////
void CEmitter::SetSVel(float _VelX, float _VelY){
	m_fSVelX = _VelX;
	m_fSVelY = _VelY;
}
/////////////////////////////////////////////////////////////////////////////
// Function		: Set Position
// Notes		: sets the start position for particles
/////////////////////////////////////////////////////////////////////////////
void CEmitter::SetPos(float _PosX, float _PosY){
	m_fPosX = _PosX;
	m_fPosY = _PosY;
}
void CEmitter::SetPos2(float _PosX, float _PosY){
	m_fPosX2 = _PosX;
	m_fPosY2 = _PosY;
}
/////////////////////////////////////////////////////////////////////////////
// Function		: Set Color
// Notes		: Sets the start and end colors for particles
/////////////////////////////////////////////////////////////////////////////
void CEmitter::SetColor(int _StA, int _StR, int _StG, int _StB, int _EndA, int _EndR, int _EndG, int _EndB){
	m_nStAlpha = _StA;
	m_nStRed = _StR;
	m_nStGreen = _StG;
	m_nStBlue = _StB;
	m_nEndAlpha = _EndA;
	m_nEndRed = _EndR;
	m_nEndGreen = _EndG;
	m_nEndBlue = _EndB;
}
/////////////////////////////////////////////////////////////////////////////
// Function		: Load XML
// Notes		: Reads in and loads an XML document
/////////////////////////////////////////////////////////////////////////////
bool CEmitter::LoadXML(std::string filepath){
	bool works = true;
	//Open the xml
	TiXmlDocument doc;
	works = doc.LoadFile(filepath.c_str());
	if(works){
		//Read in the data
		TiXmlElement* pRoot = doc.RootElement();
		if(pRoot == nullptr){return false;}
		wostringstream stream;
		double tempFloat = 0.0;
		TiXmlElement* pEmit = pRoot->FirstChildElement("Emitter");
		TiXmlElement* pPart = pRoot->FirstChildElement("Particle");
		TiXmlElement* pColor = pRoot->FirstChildElement("Color");
		//Emitter Info.
		stream << pEmit->Attribute("Loop", &tempFloat);
		m_bLooping = (stream.str() == _T("true") || stream.str() == _T("True"));
		stream.str(_T(""));
		stream << pEmit->Attribute("Invert", &tempFloat);
		m_bInvert = (stream.str() == _T("true") || stream.str() == _T("True"));
		stream.str(_T(""));
		stream << pEmit->Attribute("Shape");
		pEmit->Attribute("PosX", &tempFloat);
		m_fPosX = (float)tempFloat;
		pEmit->Attribute("PosY", &tempFloat);
		m_fPosY = (float)tempFloat;
		pEmit->Attribute("MaxPartCount", &tempFloat);
		m_unMaxPart = (unsigned int)tempFloat;
		pEmit->Attribute("Delay", &tempFloat);
		m_fSpawnDelay = (float)tempFloat;
		//Emitter Extra Information
		if(stream.str() == _T("Circle")){
			m_eShape = ECIRCLE;
			pEmit->Attribute("Range", &tempFloat);
			m_fRange = (float)tempFloat;
		}
		else if(stream.str() == _T("Square")){
			m_eShape = ESQUARE;
			pEmit->Attribute("Width", &tempFloat);
			m_fPosX2 = m_fPosX + (float)tempFloat;
			pEmit->Attribute("Height", &tempFloat);
			m_fPosY2 = m_fPosY + (float)tempFloat;
		}
		else if(stream.str() == _T("Line (Horizon)")){
			m_eShape = EHLINE;
			pEmit->Attribute("Length", &tempFloat);
			m_fPosX2 = m_fPosX + (float)tempFloat;
			m_fPosY2 = m_fPosY;
		}
		else if(stream.str() == _T("Line (Vert)")){
			m_eShape = EVLINE;
			m_fPosX2 = (float)tempFloat;
			pEmit->Attribute("Length", &tempFloat);
			m_fPosY2 = m_fPosY + (float)tempFloat;
		}
		else if(stream.str() == _T("Point")){
			m_eShape = EPOINT;
		}
		//Particle Information
		stream.str(_T(""));
		stream << pPart->Attribute("Image");
		m_nImage = CSGD_TextureManager::GetInstance()->LoadTexture(stream.str().c_str());
		pPart->Attribute("PartWidth", &tempFloat);
		m_nPartWidth = (int)tempFloat;
		pPart->Attribute("PartHeight", &tempFloat);
		m_nPartHeight = (int)tempFloat;
		pPart->Attribute("StartScale", &tempFloat);
		m_fStScale = (float)tempFloat;
		pPart->Attribute("EndScale", &tempFloat);
		m_fEndScale = (float)tempFloat;
		pPart->Attribute("LifeMin", &tempFloat);
		m_fLifeMin = (float)tempFloat;
		pPart->Attribute("LifeMax", &tempFloat);
		m_fLifeMax = (float)tempFloat;
		pPart->Attribute("StartVelX", &tempFloat);
		m_fSVelX = (float)tempFloat;
		pPart->Attribute("StartVelY", &tempFloat);
		m_fSVelY = (float)tempFloat;
		pPart->Attribute("EndVelX", &tempFloat);
		m_fEndVelX = (float)tempFloat;
		pPart->Attribute("EndVelY", &tempFloat);
		m_fEndVelY = (float)tempFloat;
		pPart->Attribute("StartRotate", &tempFloat);
		m_fStRot = (float)tempFloat;
		pPart->Attribute("EndRotate", &tempFloat);
		m_fEndRot = (float)tempFloat;
		//Color
		int alpha, red, green, blue;
		pColor->Attribute("StartA", &alpha);
		pColor->Attribute("StartR", &red);
		pColor->Attribute("StartG", &green);
		pColor->Attribute("StartB", &blue);
		m_nStAlpha = alpha;
		m_nStRed = red;
		m_nStGreen = green;
		m_nStBlue = blue;
		pColor->Attribute("EndA", &alpha);
		pColor->Attribute("EndR", &red);
		pColor->Attribute("EndG", &green);
		pColor->Attribute("EndB", &blue);
		m_nEndAlpha = alpha;
		m_nEndRed = red;
		m_nEndGreen = green;
		m_nEndBlue = blue;
	}
	return works;
}